<?php
namespace app\extensions\lib\push;
use app\exception\ApiException;
use app\extensions\lib\RedisLock;
use app\model\Package;
use app\model\TokenInfo;
use app\queue\redis\shushu\DeviceEvent;
use Firebase\JWT\JWT;
use support\Db;
use support\Log;
use support\Redis;

class IosPush extends BasePush
{
    const JWT_TIME = 40 * 60;
    const ACCESS_TOKEN_KEY = 'ios_push_access_token_';
    const LOCK_KEY = 'lock_ios_access_token_';
    const ASYNC_KEY = '_push_list_';

    public static function push($tokenInfo, array $message,$request_id='')
    {
        self::blackSkip($tokenInfo,$request_id);
        self::requestRecall($tokenInfo,$request_id);
        if(!$tokenInfo['tokens']) return self::defaultReturn();

        $authToken = self::getPackageIdentification($tokenInfo['package_id'],$tokenInfo['is_async']);
        return self::send($tokenInfo['package_id'],$authToken,$tokenInfo,$message,false,$request_id);
    }

    public static function getPackageIdentification($package_id,$is_async)
    {

        $package_configList = Package::getPackageConfig(2);
        $config = $package_configList[$package_id] ?? [];
        if(!isset($config['package_name'])){
            throw new ApiException("包：{$package_id}无对应 包名");
        }
        if(!isset($config['key_id']) || !isset($config['team_id'])){
            throw new ApiException("包：{$package_id}无对应 密钥id,团队id");
        }

        $cacheKey = self::getCacheKey($package_id,$is_async,$config);
        $oldValue = Redis::get($cacheKey);
        if($oldValue){
            return $oldValue;
        }

        $RedisLock = new RedisLock();
        $lockKey = self::ACCESS_TOKEN_KEY;;
        //使用悲观锁
        $authorization = $RedisLock->actionLock($lockKey,function () use ($package_id,$cacheKey,$config){
            $oldValue = Redis::get($cacheKey);
            if($oldValue) return $oldValue;//其他锁已经生成就不用了

            $key_id = $config['key_id'];
            $team_id = $config['team_id'];

            //$private_key_file = "AuthKey_".$key_id.".p8";
            /*$private_key_file = $config['private_key_file'] ?? '';
            $config_path = app_path('extensions/lib/package_config');
            $private_key = file_get_contents($config_path.'/'.$private_key_file);*/
            $private_key = $config['private_key'] ?? '';
            $now_seconds = time();
            $payload = array(
                "iss" => $team_id,
                "iat" => $now_seconds,
            );
            $authorization = JWT::encode($payload, $private_key,  'ES256', $key_id);
            $time = self::JWT_TIME - rand(6,20);
            Redis::set($cacheKey,$authorization);
            Redis::expire($cacheKey,$time);
            return $authorization;
        });

        return $authorization;
    }

    private static function send($package_id, $authToken, $tokenInfo, array $message,$is_sandbox=false,$request_id='')
    {
        $tokens = $tokenInfo['tokens'];
        $udids = $tokenInfo['udids'];
        $tokens = array_filter(explode(',',$tokens));
        $udids = array_filter(explode(',',$udids));
        $package_configList = Package::getPackageConfig(2);
        if(!isset($package_configList[$package_id]['package_name'])){
            throw new ApiException("包：{$package_id}无对应 包名");
        }
        $Event = new DeviceEvent();
        $bundleID = $package_configList[$package_id]['package_name'];
        $sockets = [];
        $eventIds = [];

        $header = array(
            "Content-Type: application/json",
            "apns-expiration: 0",
            "apns-push-type: alert",
            "apns-topic: ".$bundleID, // 4.我们App的bundleID
            "authorization: bearer ".$authToken // 5.上面生成并加密的JWT
        );
        /*dump($tokens,$header,$message);
        return 1;*/
        //消息体信息
        $message_type = isset($message['aps']['alert']) ? 0 : 1;
        $title = $message['aps']['alert']['title'] ?? '';
        $content = $message['aps']['alert']['body'] ?? '';
        $image = '';
        if(isset($message['media']) && ($message['media']['type'] ?? '')=='image'){
            $image = $message['media']['url'] ?? '';
        }

        $multiSockets = curl_multi_init();
        $url = !$is_sandbox ? 'https://api.push.apple.com:443' : 'https://api.sandbox.push.apple.com:443';
        foreach ($tokens as $key => $token) {
            $sockets[$key] = curl_init();
            $udid = $udids[$key] ?? 'no udid';
            $eventIds[$key] = $eventId = getEventId($package_id,uniqid($udid));//事件Id
            $path = '/3/device/'.$token;
            $message['aps']['message_id'] = $eventId;//透传消息id
            $message['aps']['udid'] = $udid;//透传消息udid
            if(env('APP_ENV')!='production' && !$is_sandbox){
                dump($message);
            }
            $payload = json_encode($message);
            curl_setopt_array($sockets[$key], array(
                // 2.带上deviceToken向APNs服务器请求
                CURLOPT_URL => $url.$path,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0,
                CURLOPT_TIMEOUT => 5,
                CURLOPT_CUSTOMREQUEST => "POST",
                // 3.把推送的payload放入请求体+设置请求头
                CURLOPT_POSTFIELDS => $payload,
                CURLOPT_HTTPHEADER => $header,
            ));
            curl_multi_add_handle($multiSockets, $sockets[$key]);
        }

        $running = null;
        $request_time = date('Y-m-d H:i:s');
        do {
            curl_multi_exec($multiSockets, $running);
        } while ($running > 0);

        $valid = $error = $success = 0;
        $delToken = [];
        foreach ($sockets as $key => $h) {
            $info = curl_getinfo($h);
            $eventId = $eventIds[$key];
            $total_time = (($info['total_time'] ?? 0) * 1000).'ms';
            $response_error = curl_multi_getcontent($h);
            $code = '';
            $status = 0;
            $re_send = false;
            $token = $tokens[$key] ?? 'no found token';
            $udid = $udids[$key] ?? 'no udid';
            $prefixLog = "$request_id:$udid:$token===time:$total_time:".json_encode($response_error);
            if($response_error){
                $result = json_decode($response_error,true);
                Log::channel('ios')->warning($prefixLog);
                //正式服失败，再重新测试服
                if(!$is_sandbox && isset($result['reason']) && $result['reason'] =='BadDeviceToken'){
                    $re_send = true;
                    $test_tokenInfo = $tokenInfo;
                    $test_tokenInfo['tokens'] = $token;//只发送单个

                    $test_result = self::send($package_id, $authToken, $test_tokenInfo, $message,true,$request_id);
                    $success +=$test_result['success'] ?? 0;
                    $error += $test_result['error'] ?? 0;
                    Log::channel('ios')->notice($prefixLog.'重新沙河地址结果:'.json_encode($test_result));
                }
                if(!$re_send){
                    if(isset($result['reason']) && $result['reason'] =='Unregistered'){
                        $delToken[] = $token;
                        $valid++;
                    }
                    $error++;
                }

            }else{
                $success++;
                $status = 1;
                Log::channel('ios')->info($prefixLog);
            }
            curl_multi_remove_handle($multiSockets, $h);

            //设备记录上传
            if(!$re_send){
                $other = json_decode(Redis::get(Push::getPayloadKey($request_id)),true) ?:[];
                $properties = [
                        'title' => $title,
                        'content' => $content,
                        'image' => $image,
                        'message_type' => $message_type,
                        'request_id' => $request_id,
                        '#ip' => '',
                        'os_id' => '2',
                        'package_id' => $package_id,
                        'token' => $token,
                        'code' => strval($code),
                        'status' => strval($status),
                        'sdk_info' => ['bundleID' => $bundleID],
                        'result' => $response_error,
                        'request_time' => $request_time
                    ] + $other;
                \Webman\RedisQueue\Redis::send($Event->queue,[
                    'package_id' => $package_id,
                    'params' => [
                        'track_update',
                        '',
                        $udid,
                        $properties,
                        BasePush::EVENT_DEVICE_PUSH_LOG,
                        $eventId
                    ],
                ]);
            }

        }

        if (is_resource($multiSockets)) {
            curl_multi_close($multiSockets);
        }
        if (!empty($sockets)) {
            foreach ($sockets as $val) {
                if (is_resource($val)) {
                    curl_close($val);
                }
            }
        }
        if($delToken){
            $TokenInfo = new TokenInfo();
            $TokenInfo->refreshInvalid($delToken);
        }
        return compact('success','error','valid');

    }

    private static function getCacheKey($package_id,$is_async,$config)
    {
        $cacheKey = self::ACCESS_TOKEN_KEY.$is_async.'_'.($config['team_id'] ?? '').'_'.($config['key_id'] ?? '');
        return $cacheKey;
    }


    public function registerDevice($name,$bundleID,$authToken,$deviceTokens){





        $deviceGroupToken = "your_device_group"; //
        $payload = [ 'device_tokens' => $deviceTokens, ]; // 将负载数据转换为 JSON
        $jsonPayload = json_encode($payload); // APNs 主机和端口
        $apnsHost = 'api.push.apple.com';
        //$url = !$is_sandbox ? 'https://api.push.apple.com:443' : 'https://api.sandbox.push.apple.com:443';
        $apnsPort = 443; // APNs 路径
        $apnsPath = "/3/deviceGroup/$deviceGroupToken"; //


        $header = array(
            "Content-Type: application/json",
            "apns-expiration: 0",
            "apns-push-type: voip",
            "apns-topic: ".$bundleID, // 4.我们App的bundleID
            "authorization: bearer ".$authToken // 5.上面生成并加密的JWT
        );

        $ch = curl_init(); // 配置 cURL 请求
        curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonPayload);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_URL, "https://$apnsHost:$apnsPort$apnsPath");
        // 执行请求
        curl_exec($ch);
        $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if ($responseCode !== 200) {
            // 处理错误情况
            echo "请求失败，响应码：$responseCode";
        } // 关闭 cURL 句柄
        dump(curl_getinfo($ch));
        curl_close($ch);

        return $responseCode;
    }
}
